package cn.com.libertymutual.production.service.impl.business;

import it.sauronsoftware.ftp4j.FTPClient;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeanUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import cn.com.libertymutual.core.util.uuid.UUID;
import cn.com.libertymutual.production.exception.UserException;
import cn.com.libertymutual.production.model.nomorcldatasource.Prpdefile;
import cn.com.libertymutual.production.model.nomorcldatasource.PrpdefileWithBLOBs;
import cn.com.libertymutual.production.model.nomorcldatasource.Prpdkindlibrary;
import cn.com.libertymutual.production.model.nomorcldatasource.PrpdkindlibraryWithBLOBs;
import cn.com.libertymutual.production.pojo.request.PrpdEFileRequest;
import cn.com.libertymutual.production.pojo.response.Response;
import cn.com.libertymutual.production.service.api.business.EFileBusinessService;
import cn.com.libertymutual.production.utils.Constant.BUSINESSLOGTYPE;
import cn.com.libertymutual.production.utils.Constant.OPERTYPE;
import cn.com.libertymutual.production.utils.ObjectUtil;
import cn.com.libertymutual.production.utils.ftp.FTP4JUtil;

import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.xiaoleilu.hutool.date.DateUtil;
import com.xiaoleilu.hutool.util.CollectionUtil;

/**
 * 事务管理注意事项：
 * 1. 新增/修改操作必须启用事务
 * 2. 方法体内部抛出异常即可启用事务
 * @author Steven.Li
 * @date 2017年7月28日
 * 
 */
@Service
@Transactional(rollbackFor = Exception.class, transactionManager = "transactionManagerCoreData")
public class EFileBusinessServiceImpl extends EFileBusinessService {
	
	@Override
	public Response findPrpdEFile(PrpdEFileRequest prpdEFileRequest) {
		Response result = new Response();
		try {
			PageInfo<PrpdefileWithBLOBs> pageInfo = prpdEFileService
					.findPrpdEfile(prpdEFileRequest);
			result.setTotal(pageInfo.getTotal());
			result.setResult(pageInfo.getList());
			result.setSuccess();
		} catch (Exception e) {
			log.error("系统异常错误！", e);
			result.setAppFail();
			result.setResult(e.getMessage());
		}
		return result;
	}

	@Override
	public Response insertEfile(MultipartHttpServletRequest fileRequest,
			PrpdEFileRequest prpdEFileRequest) throws Exception {
		Response result = new Response();
		PrpdefileWithBLOBs prpdEFile = new PrpdefileWithBLOBs();
		BeanUtils.copyProperties(prpdEFileRequest, prpdEFile);
		prpdEFile.setInserttime(new Date());
		prpdEFile.setUpdatedate(new Date());
		if (!StringUtils.isEmpty(prpdEFileRequest.getYear())) {
			prpdEFile.setYear(DateUtil.year(prpdEFileRequest.getYear()) + "");
		}
		try {
			if (!StringUtils.isEmpty(prpdEFileRequest.getKindcode())) {
				String[] kindcodes = prpdEFileRequest.getKindcode().split(",");
				String[] kindcnames = prpdEFileRequest.getKindcname()
						.split(",");
				String[] kindversions = prpdEFileRequest.getKindversion()
						.split(",");
				String eFileCode = "";
				String code = "";
				Map<String, MultipartFile> map = new HashMap<String, MultipartFile>();
				StringBuffer eFileDetails = new StringBuffer();
				for (int i = 0; i < kindcodes.length; i++) {
					// 通过匹配等级和条款代码生成备案号
					code = prpdEFileRequest.getMatchlevel().toUpperCase()
							+ kindcodes[i];
					// 生成备案号代码
					eFileCode = code + prpdEFileService.findEFileCode(code);
					prpdEFile.setEfilecode(eFileCode);
					prpdEFile.setKindcode(kindcodes[i]);
					prpdEFile.setKindcname(kindcnames[i]);
					prpdEFile.setKindversion(kindversions[i]);

					// 保存上传的文件信息
					MultipartFile file = fileRequest.getFile("img"
							+ kindcodes[i]);
					if (file != null && !file.isEmpty()) {
						String fileName = file.getOriginalFilename();
						String typeName = fileName.substring(fileName
								.indexOf(".") + 1); 
						if ("bmp".toLowerCase().equals(typeName.toLowerCase())){
							BufferedImage img = ImageIO.read(file.getInputStream());
							int iwidth =(int) Math.ceil(((double)img.getWidth())  / 96 * 2.54);
			    			int iheight =(int) Math.ceil(((double)img.getHeight())  / 96 * 2.54);
			    			if (iwidth>23||iheight>23){
			    				throw new UserException("图片尺寸不超过 高：23， 宽：23,请重新上传!");
			    			} else {
			    				prpdEFile.setImgwidth(new BigDecimal(iwidth));
			    				prpdEFile.setImglength(new BigDecimal(iheight));
			    			}
						}
						prpdEFile.setPath(productionFtpSetting.getIndigoHost());
						prpdEFile.setImgname(eFileCode + "." + typeName);
						map.put(eFileCode, file);
						
						//配置备案号并上传图片后修改条款是否配置备案号状态
						PrpdkindlibraryWithBLOBs prpdKindLibrary = prpdKindLibraryService.fetchKinds(kindcodes[i], kindversions[i], null).get(0);
						prpdKindLibrary.setEfilestatus("Y");
						prpdKindLibraryService.updateEFileStatus(prpdKindLibrary, kindcodes[i], kindversions[i]);
					}
					
					prpdEFileService.insert(prpdEFile);
					
					eFileDetails.append("新增备案号基础数据:"
							+ ObjectUtil.info(prpdEFile) + ";");
					
					// ********************************************************************************
					// 备案号新增日志-保存基础数据
					systemLog.save(
							BUSINESSLOGTYPE.EFILE,
							OPERTYPE.INSERT,
							prpdEFile.getEfilecode(),
							eFileDetails.toString());
					// ********************************************************************************
					//每次保存成功后清除上一次的记录
					eFileDetails = new StringBuffer();

				}
				
				// 上传文件efiling
				ByteArrayResource arrayResource = null;
				for (Entry<String, MultipartFile> multipartFile : map
						.entrySet()) {
					String eFileNum = multipartFile.getKey();
					MultipartFile file = multipartFile.getValue();
					log.info(file.getOriginalFilename());
					try {
						arrayResource = new ByteArrayResource(file.getBytes()) {
							@Override
							public String getFilename()
									throws IllegalStateException {
								if (file.getOriginalFilename().lastIndexOf('.') > 1) {
									// 目前资料系统接收中文文件名会出现乱码，故给他重新生成一个文件名 bob.kuang
									// 20170413
									return UUID.getUUIDString() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.'));
								}
								return UUID.getUUIDString();
							}
						};
						MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
						param.add("files", new HttpEntity<ByteArrayResource>(
								arrayResource));
						param.add("entity.systemCode", "eFiling");
						param.add("batch", UUID.getUUIDString());
						param.add("handlerClass", "eFiling");

						/*
						 * entity.systemCode ：系统名称(例如：eFiling) 
						 * entity.operator：操作员代码(对应prpduser的代码) 
						 * entity.businessNo：业务号(投保单号/保单号/批单号)
						 *  entity.fileTitle ：文件名称(例如：XXYY.JPG名称就传XXYY) 
						 *  entity.property00：文件类型对应的代码(对应t_f_file_type的code字段) 
						 *  entity.property01：1和0 1：是核保勾选的必传资料 0：不是核保勾选的资料 
						 *  entity.property03：固定字段(upload) 
						 *  entity.property05：模块上传(如果是新ui上传就传新ui标示的一个东西，第三方就让第三方传不一样的固定名称，作为区别到底是哪个模块上传的资料) 
						 *  files ： 传送的文件
						 * password ：得到安全域校验的密码 目前传1(对应T_FILE_SYSTEM表的password)
						 */
						param.add("entity.operator", "0000000000"); //nasay usercode
						param.add("entity.businessNo", eFileNum);
						param.add("entity.fileTitle",file.getOriginalFilename().substring(0,file.getOriginalFilename().indexOf(".")));
						param.add("entity.property00", "248"); // 文件类型对应的代码(对应t_f_file_type的code字段)
																// 暂时选其他
						param.add("entity.property01", "0");
						param.add("entity.property03", "upload");
						param.add("entity.property05", "ProductionManagement");
						param.add("password", "1");
						HttpHeaders httpHeaders = new HttpHeaders();
						httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
						httpHeaders.setAcceptCharset(Lists.newArrayList(Charset.forName("UTF-8")));
						HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(param, httpHeaders);
						ResponseEntity<String> responseEntity = uploadTemplate.exchange(productionFtpSetting.geteFilingURL(), HttpMethod.POST, httpEntity,String.class);
						String responseFileDto = responseEntity.getBody();
						log.info(responseFileDto);

						// 上传文件到打印服务器
						File imgFile = multipartToFile(file, eFileNum);
						FTPClient ftpClient = FTP4JUtil.makeFtpConnection(productionFtpSetting.getIndigoHost(),
																			productionFtpSetting.getIndigoPort(), 
																			productionFtpSetting.getIndigoUser(), 
																			productionFtpSetting.getIndigoPwd());
						FTP4JUtil.upload(ftpClient, imgFile, productionFtpSetting.getIndigoDir());
						imgFile.delete();
						
						eFileDetails.append("上传备案号图片/文件到EFiling和FTP:{"
								+ eFileNum + file.getOriginalFilename().substring(file.getOriginalFilename().indexOf(".")) + "};");
						// ********************************************************************************
						// 备案号图片上传日志-保存到EFiling和FTP
						systemLog.save(
								BUSINESSLOGTYPE.EFILE,
								OPERTYPE.INSERT,
								eFileNum,
								eFileDetails.toString());
						// ********************************************************************************
						//每次保存成功后清除上一次的记录
						eFileDetails = new StringBuffer();
					} catch (Exception e) {
						log.error(e.getMessage(), e);
						throw e;
					}
				}
			}
		} catch (Exception e) {
			log.error("系统异常错误！", e);
			throw e;
		}
		return result;
	}

	/**
	 * MultipartFile 转换成File
	 * 
	 * @param multfile
	 *            原文件类型
	 * @return File
	 * @throws IOException
	 */
	private File multipartToFile(MultipartFile multfile, String fileName) throws Exception {
		String fullFileName = multfile.getOriginalFilename();
		String fileType = fullFileName.substring(fullFileName.indexOf(".") + 1);
		// 手动创建临时文件
		File tmpFile = new File(System.getProperty("java.io.tmpdir")
				+ System.getProperty("file.separator") + fileName + "." + fileType);
		InputStream ins = multfile.getInputStream();
		 try {
			OutputStream os = new FileOutputStream(tmpFile);
			int bytesRead = 0;
			byte[] buffer = new byte[8192];
			while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
				os.write(buffer, 0, bytesRead);
			}
			os.close();
			ins.close();
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return tmpFile;
	}

	@Override
	public Response updateEfile(MultipartHttpServletRequest fileRequest,PrpdEFileRequest prpdEFileRequest) throws Exception {
		Response result = new Response();
		PrpdefileWithBLOBs prpdEFile = new PrpdefileWithBLOBs();
		List<PrpdefileWithBLOBs> fetchEFiles = prpdEFileService.fetchEFiles(prpdEFileRequest.getEfilecode(), 
																	prpdEFileRequest.getKindcode(), 
																	prpdEFileRequest.getKindversion());
		efileRequestProcess(prpdEFileRequest);
		BeanUtils.copyProperties(prpdEFileRequest, prpdEFile);
		prpdEFile.setUpdatedate(new Date());
		if (!StringUtils.isEmpty(prpdEFileRequest.getYear())) {
			prpdEFile.setYear(DateUtil.year(prpdEFileRequest.getYear()) + "");
		}
		try {
			if (!StringUtils.isEmpty(prpdEFileRequest.getKindcode())) {
				// 保存上传的文件信息
				MultipartFile file = fileRequest.getFile("img"
						+ prpdEFileRequest.getKindcode());
				if (file != null && !file.isEmpty()) {
					String fileName = file.getOriginalFilename();
					String typeName = fileName.substring(fileName
							.indexOf(".") + 1); 
					if ("bmp".toLowerCase().equals(typeName.toLowerCase())){
						BufferedImage img = ImageIO.read(file.getInputStream());
						int iwidth =(int) Math.ceil(((double)img.getWidth())  / 96 * 2.54);
		    			int iheight =(int) Math.ceil(((double)img.getHeight())  / 96 * 2.54);
		    			if (iwidth>23||iheight>23){
		    				throw new UserException("图片尺寸不超过 高：23， 宽：23,请重新上传!");
		    			} else {
		    				prpdEFile.setImgwidth(new BigDecimal(iwidth));
		    				prpdEFile.setImglength(new BigDecimal(iheight));
		    			}
					}
					prpdEFile.setPath(productionFtpSetting.getIndigoHost());
					prpdEFile.setImgname(prpdEFileRequest.getEfilecode() + "." + typeName);
				}
				prpdEFileService.update(prpdEFile);
				
				//将旧数据中的文件名保存至新对象，避免前端未修改上传文件时，导致新对象中上传文件名为空，被误判为修改，导致日志显示不正确的问题。
				prpdEFile.setImgname(fetchEFiles.get(0).getImgname());
				//如果修改时年度为null，则说明修改时未对原数据的年度进行修改，为避免日志显示不正确，将原数据中的年度赋值给新对象。
				if(prpdEFileRequest.getYear() == null) {
					prpdEFile.setYear(fetchEFiles.get(0).getYear());
				}
				// ********************************************************************************
				// 备案号基础数据修改日志
				String beforeContent = ObjectUtil.infoAfterCompared(fetchEFiles.get(0), prpdEFile);
				String afterContent = ObjectUtil.infoAfterCompared(prpdEFile,fetchEFiles.get(0));
				if(!"".equals(afterContent) || !"".equals(beforeContent)) {
					systemLog.save(
							BUSINESSLOGTYPE.EFILE,
							OPERTYPE.UPDATE,
							prpdEFile.getEfilecode(),
							"备案号-修改前:" + beforeContent + ";"
							+ "备案号-修改后:" + afterContent);
				}
				// ********************************************************************************
				
				if (file != null && !file.isEmpty()) {
					// 上传文件efiling
					ByteArrayResource arrayResource = null;
					log.info(file.getOriginalFilename());
					try {
						arrayResource = new ByteArrayResource(file.getBytes()) {
							@Override
							public String getFilename()throws IllegalStateException {
								if (file.getOriginalFilename().lastIndexOf('.') > 1) {
									// 目前资料系统接收中文文件名会出现乱码，故给他重新生成一个文件名 bob.kuang
									// 20170413
									return UUID.getUUIDString() + file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.'));
								}
								return UUID.getUUIDString();
							}
						};
						MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
						param.add("files", new HttpEntity<ByteArrayResource>(
								arrayResource));
						param.add("entity.systemCode", "eFiling");
						param.add("batch", UUID.getUUIDString());
						param.add("handlerClass", "eFiling");
						
						/*
						 * entity.systemCode ：系统名称(例如：eFiling) 
						 * entity.operator：操作员代码(对应prpduser的代码) 
						 * entity.businessNo：业务号(投保单号/保单号/批单号)
						 *  entity.fileTitle ：文件名称(例如：XXYY.JPG名称就传XXYY) 
						 *  entity.property00：文件类型对应的代码(对应t_f_file_type的code字段) 
						 *  entity.property01：1和0 1：是核保勾选的必传资料 0：不是核保勾选的资料 
						 *  entity.property03：固定字段(upload) 
						 *  entity.property05：模块上传(如果是新ui上传就传新ui标示的一个东西，第三方就让第三方传不一样的固定名称，作为区别到底是哪个模块上传的资料) 
						 *  files ： 传送的文件
						 * password ：得到安全域校验的密码 目前传1(对应T_FILE_SYSTEM表的password)
						 */
						param.add("entity.operator", "0000000000"); //nasay usercode
						param.add("entity.businessNo", prpdEFileRequest.getEfilecode());
						param.add("entity.fileTitle",file.getOriginalFilename().substring(0,file.getOriginalFilename().indexOf(".")));
						param.add("entity.property00", "248"); // 文件类型对应的代码(对应t_f_file_type的code字段)
						// 暂时选其他
						param.add("entity.property01", "0");
						param.add("entity.property03", "upload");
						param.add("entity.property05", "ProductionManagement");
						param.add("password", "1");
						HttpHeaders httpHeaders = new HttpHeaders();
						httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
						httpHeaders.setAcceptCharset(Lists.newArrayList(Charset.forName("UTF-8")));
						HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(param, httpHeaders);
						ResponseEntity<String> responseEntity = uploadTemplate.exchange(productionFtpSetting.geteFilingURL(), 
																						HttpMethod.POST, 
																						httpEntity,String.class);
						String responseFileDto = responseEntity.getBody();
						log.info(responseFileDto);
						
						// 上传文件到打印服务器
						File imgFile = multipartToFile(file, prpdEFileRequest.getEfilecode());
						FTPClient ftpClient = FTP4JUtil.makeFtpConnection(productionFtpSetting.getIndigoHost(),
																			productionFtpSetting.getIndigoPort(), 
																			productionFtpSetting.getIndigoUser(), 
																			productionFtpSetting.getIndigoPwd());
						FTP4JUtil.upload(ftpClient, imgFile, productionFtpSetting.getIndigoDir());
						imgFile.delete();
						
						//配置备案号并上传图片后修改条款是否配置备案号状态
						PrpdkindlibraryWithBLOBs prpdKindLibrary = prpdKindLibraryService
								.fetchKinds(prpdEFileRequest.getKindcode(), prpdEFileRequest.getKindversion(), null).get(0);
						prpdKindLibrary.setEfilestatus("Y");
						prpdKindLibraryService.updateEFileStatus(prpdKindLibrary, 
																 prpdEFileRequest.getKindcode(), 
																 prpdEFileRequest.getKindversion());
						
						StringBuffer eFileDetails = new StringBuffer();
						eFileDetails.append("上传备案号图片/文件到EFiling和FTP:{"
								+ prpdEFileRequest.getEfilecode() 
								+ file.getOriginalFilename().substring(file.getOriginalFilename().indexOf(".")) + "};");
						// ********************************************************************************
						// 备案号图片上传日志-保存到EFiling和FTP
						systemLog.save(
								BUSINESSLOGTYPE.EFILE,
								OPERTYPE.UPDATE,
								prpdEFileRequest.getEfilecode(),
								eFileDetails.toString());
						// ********************************************************************************
					} catch (Exception e) {
						log.error(e.getMessage(), e);
						throw e;
					}
				}
			}

		} catch (Exception e) {
			log.error("系统异常错误！", e);
			throw e;
		}
		return result;
	}
	
	@Override
	public Response download(String fileNm, HttpServletResponse response) {
		Response result = new Response();

		String destFilePath = productionFtpSetting.getIndigoDir() + File.separator + fileNm;
		String tempFilePath = System.getProperty("java.io.tmpdir") + System.getProperty("file.separator");

		FTPClient client;
		
		FileInputStream fis = null;
		BufferedInputStream bis = null;
		try {
			client = FTP4JUtil.makeFtpConnection(productionFtpSetting.getIndigoHost(),
													productionFtpSetting.getIndigoPort(), 
													productionFtpSetting.getIndigoUser(), 
													productionFtpSetting.getIndigoPwd());
			FTP4JUtil.download(client, destFilePath, tempFilePath);
			String tempFile = tempFilePath + File.separator + fileNm;

			File file = new File(tempFile);

			response.setContentType("application/force-download");// 设置强制下载不打开
			response.addHeader("Content-Disposition", "attachment;fileName=" + fileNm);// 设置文件名
			byte[] buffer = new byte[1024];
			
			fis = new FileInputStream(file);
			bis = new BufferedInputStream(fis);
			OutputStream os = response.getOutputStream();
			int i = bis.read(buffer);
			while (i != -1) {
				os.write(buffer, 0, i);
				i = bis.read(buffer);
			}
			result.setSuccess();
		} catch (Exception e) {
			log.error("系统异常错误！", e);
			result.setAppFail();
			result.setResult(e.getMessage());
		} finally {
			if (bis != null) {
				try {
					bis.close();
				} catch (IOException e) {
					log.error(e.getMessage());
				}
			}
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					log.error(e.getMessage());
				}
			}
		}

		return result;
	}
	
	/**
	 * 处理前端传递的参数，将类型为String且值为null的转换为空字符串
	 * @param obj
	 * @throws Exception
	 */
	private void efileRequestProcess(Object obj) throws Exception {
		Field[] objFields = obj.getClass().getDeclaredFields();
		List<Field> fields = new ArrayList<Field>();
		fields.addAll(CollectionUtil.newArrayList(objFields));
		
		for (Field field : fields) {
			
			String fieldNm = field.getName();
					
			try {
				Method getMethod = obj.getClass().getMethod("get" + fieldNm.substring(0, 1).toUpperCase() + fieldNm.substring(1));
				Method setMethod = obj.getClass().getMethod("set" + fieldNm.substring(0, 1).toUpperCase() + fieldNm.substring(1), field.getType());
				Object val = getMethod.invoke(obj);
				String fieldTypeName = field.getType().toString(); //获取目标对象字段类型
				fieldTypeName = fieldTypeName.substring(fieldTypeName.lastIndexOf('.') + 1);
				if(!"Date".equals(fieldTypeName) && val == null) {
					setMethod.invoke(obj, "");
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw e;
			}
		}
	}
}
